﻿Q-Vert: (continuation of) the April Fool's "joke" for 2023, Mother's Day 2023 edition
  Despite what the internet thinks, iQue can't play N64 games in much the same way that pixels aren't square.  That's where this steps in.  It will automatically convert most NTSC versions of GoldenEye to run on both N64 & iQue consoles with minimal input from you, the end-user.
  The idea behind this is to modify some of the libultra functions to support both platforms.  Most lib function changes are selected based on __osBbIsBb at 80000388, determined in init().
  tl;dr: you can save properly on either console and pak devices are supported.  It also adds support for both 4k & 16k EEPROMs.


.: Version Log :.
2023 04 01	initial release

2023 05 14	version 1.1
	*) Added: create_callback.py, init_rate_multipliers.py, stop_replay.py
	*) Updated: PI_thread.py (logs more symbols), partial_exception_handler.py (NMI support)
	*) Calls secure kernel reset function on a timeout after NMI.  This prevents the RCP locking on reset (no video out in iQue menus).
	*) Applies bugfix from later SDK versions to test if an NMI occurred before the event handler was created.
	*) Option to pseudo-throttle gameplay by manipulating the animation rate added to simulate N64 gameplay on iQue.  Reduces to 81.75% speed, approximately the same amount of time musically for Bond to shoot the Eye.  Doesn't affect N64-alikes and not applied during replays.
	   Rate changes are only applied when loading a ROM due to the weird way this thing patches; if you change the setting, reload the ROM.
	   Note: currently, animated objects still move at the original rate causing, for instance, Cradle's helicopter cutscene to look awfully foolish.  Rolling a fix for that into a later patch.


.: Usage :.
  !!! The directory named "GE" must be in the same directory as "Q-Vert" or you will get errors that don't make sense !!!
  Option 1: run "Q-Vert.py"
    Requires python 3.8 or higher.  Optionally, install pyCryptodome for decent encryption or keep slowaes.py in the same directory for awful encryption.
  Option 2: run "Q-Vert.exe"
    Requires some 64bit version of Windows.  It may take half a minute before the dialog actually opens, but it should.
  
  Basic use: in the left column, load a ROM, load a ticket or ticket.sys, then in the right column output a ROM or choose a directory to drop a new ticket/.sys + app.
  Messages appear in the black window, closing it will close the program so don't do that, okay?
  
  *Generating an N64 ROM:
  Provide a ROM--likely a GoldenEye one but not ruling out anything from 1997 at this point.
  USA & Japanese ROMs both work, as well as a range of hacks including Goldfinger and 640i.  PAL color output isn't supported by the iQue and it will force detected region back to NTSC.
  This operates in two passes.  It looks up the samples in "GE", masking addresses and/or registers, much like how you test regional/build differences.  It generates symbols for functions, tying them to their local addresses.
  In the second pass the detected functions are patched, using those symbols to link functions/pointers that aren't local.
  
  The output file should be usable on either a N64 or iQue console (when encrypted).  Emulators may not like the iQue register accesses, can't guarantee they work on there.
  To identify things modded this way easier, the platform has been set to "B", as in "BBPlayer", same as unQue conversions.  The hope is that emulators that are picky about those registers or don't like internal names in cp936 can just filter it out or something.  There's also a 7-byte timestamp at 0x34: year month day hrs min sec.
  
  *Autogenerating an iQue ticket:
  !!! This requires a ROM was provided !!!
  Provide either "ticket.sys" ora ticket that works with your unit ripped from "ticket.sys".  It will use your Dr. Mario 64 ticket to generate a new one that works with your device.
  Standalone tickets must be one from an existing released title or manual.  This way the titlekey is already encrypted using your unit's personal key and the decrypted value can just be plucked from a table instead.
  The whole point is to avoid all the ECDH key nonsense to vastly simplify distribution.  To use the same ticket on a different iQue they only need to change 16 bytes at 0x289C to match one already plaintext on their unit.  Your unit will kindly encrypt the app on first run.
  
  This sets the ticket icon/label, contentID, rdram size to 8MB, access flag 0x20 to enable mode 2 (changes DMA behaviour so you can use those 8MB).
  The default contentID/filename is "003e9bc0.dat": 41:shooters, 3:3rd game in cat., 1:game vs 9 manual, 4:release, inc. to a rounder number
  
  EEPROM may need to be relocated, depending on what you patched against.  The iQue a copy to memory before boot and reads it back when you reset to its own menu.  Therefore, you need to provide an address that doesn't get clobbered at runtime.
  The default location for eeprom is 806FFE00, providing 0x200 at the end of the last framebuffer in "7MB" mods and a completely safe location on 4MB.  0x800 would be more ideal.  This value can be changed as needed.
  The "latest" usable address in normal RDRAM is 807FC000.  It's loaded and saved as one block (0x4000); any further will cause a hardlock at boot and you'll need to unplug your unit to reset it.
  As an example, if you apply this to the 640i patch you'll want to change the target to 806D0000 to avoid corruption.
  
  When saving you select an output directory.  Any files with the same name will be overwritten.
  If only a ticket was provided, a new ticket will be saved.
  If a ticket.sys file was provided, it will search it for a a ticket with the same contentID.  If one is found it will be replaced, and if one isn't a new ticket will be inserted into the file.  The output file will be called "hackit.sys", and a standalone ticket will also be generated.
  If the "encrypt" option is set, a once-encrypted .app will also be created in the same directory.
  
  *Autogenerating a once-encrypted iQue .app:
  !!! This requires a ticket is provided !!!
  If pyCryptodome is installed (or anything that is named Crypto) it will use that to encrypt the file instead, likely in a couple seconds.  Note this lib is a heavyweight and loads a lot of other stuff with it you might not want.
  Otherwise, if 'aes.py' is in the same directory it will use that instead.  It's a terribly slow pure-python script pulled off Stack Overflow but does work.
  Without either, you'll just have to encrypt it yourself later.
  
  *To use an existing save file on iQue, pad the file to 0x4000 and rename it "{contentID}.sta".


.: Implemented so far :.
* system initialization
  Added detection and initialization of iQue, its registers, etc.
  When run as N64, system constants specific to iQue starting at 8000035C are all initialized to prevent false positives.
* General Exception Handler:
  Partial support for MI hardware events.  See TODO list below.
* VI change settings:
  Status flag 13 (2000) must NOT be set on iQue!  It is masked away before changing VI settings.  GE is a bit unique in that they directly push VI settings to VI update at least once, so both that function and the higher-level function usually modified by iQue is patched.
* PI E/DMA:
  There's an extension for handling DMA read requests using a target buffer at 80600000 when the target rdram address ends in the range 0x60~0x80.  After the DMA completes it's fastcopied from the buffer to target.  Frankly, I don't know why.  Fairly certain the "alternate PI mode" flag in the ticket forces it to report as a type 2 iQue, preventing this nonsense.  8MB games must set that to avoid corruption.
  "Extended PI" may be selected in the EDMA handler.  These are +0x50 the usual addresses if (HW + base) < 0x401.
  Also, see the TODO list below.
* Controller Support
  --Normally the last posted SI command is tracked; if it hasn't changed it doesn't need to be written to PIFRAM again, only the result read back.  For early games that read status once and only read controller input from then on, they may never post a new command again.
  iQue usually invalidated these (setting 0xFD, for instance) to force reposts every time.  Console doesn't care either way.  In this case, since only two functions read the value those are rewritten to skip the test, forcing repost.
  --Status & input tests will check __osBbHackFlags.  If nonzero, it swaps input from controller 1 with the given port.  This may be a requirement for certain types of iQue multiplayer setups.
  --iQue checks the array at 80000374 for valid pointers and sets the 'slot filled' flag accordingly.  If a pointer is provided Controller Pak data is expected at the location.  Normally, all other devices are explicitly dummied out via the iQue SDK with "failure" responses.  As a failsafe, slot device addresses are restricted to Controller Pak data range (0~0x8000); device tests should fail gracefully either way.
  To function correctly, isBB tests wrap the 'slot filled' code so N64s post their actual values.  Additional tests wrap all slot read/write functions.
  Although not used directly in this game, iQue leaves Controller Pak tests to ticket selection.  It explicitly returns "device not found" for Rumble Paks.  Later lib versions could force a rotor off event (esp. after reset) without checking the device type, and although that isn't the case in this situation there is no good reason to run the tests to failure.
* EEPROM Support
  Status, read, and write functions are all wrapped to support iQue.  Unlike its original form of support, status will return a value identical to what console would have provided.
  An extensive rewrite was required anyway, so now the game can alternately detect and support both 4k & 16k EEPROM on either console.  The function to return EEPROM type no longer produces a boolean but returns 1 or 2 like later lib versions.  Stock GE code only tests nonzero, there shouldn't be too many serious side-effects.
* NMI (as of ver. 1.1)
  All titles except Dr.Mario64 trigger an SK Exit call after an NMI (reset) to ensure a safe return to the iQue's main menus.  Failing to time this correctly can cause the RCP to lock.
  The link is a bit abstracted in order to delay return to menus.  In preNMI the Count is stored, and each iteration through the exception handler checks if enough time as elapsed (0x5000000 cycles).
* event callback assignment (as of ver. 1.1)
  When preNMI event callbacks are assigned a test is performed to check if reset was already pressed, immediately triggering the callback.  This is good programming but not mandatory.
* Psuedo-Throttling (as of ver. 1.1)
  GoldenEye, by all appearances, does not throttle framerate in any way.  Thankfully, there aren't noticable cases where incremental loops are used for timers instead of count/frame tests.  NTSC video standards don't change, and although the CPU is clocked twice as fast Count is divided by four instead of two.  Any timer against those should be fine.
  Adding proper throttling isn't practical.
  Visibly the game runs faster, mostly on account of finishing all its work in (roughly) 2/3 the time.  That irks quite a few people--likely the majority of people--so an option is added that fakes a framerate reduction by reducing the animation rate by 18.75%.  That preserves responsiveness, avoids the jitters, but plays roughly the same on either console.
  That said, at the normal rate replays don't fail, timers keep proper time, and frankly it's running a lot smoother overall.  If you prefer the upgrade, set the animation speed to normal.
  * Note in v1.1 non-actors (objects like projectiles & vehicles) are unaffected.  This can cause discrepancies in gameplay and cutscenes.

  Anything elsewhere has not been tampered with.  If it should have been, have fun implementing that ;*)

.: TODO or Don't Care :.
* Psuedo-Throttling (TODO)
  Current pseudo-throttling only affects actors, not objects.  Aircraft & projectiles are still timed via framerate, not modified by the animation multiplier.  The Runway & Cradle cutscenes won't work as expected until that's corrected.
  Actual throttling would be preferable.
* General Exception Handler (TODO)
  iQue uses cart interrupts in order to catch its new MI hardware interrupts.
  Implementing this requires several steps: first, adding the condition checks into the interrupt handler; second, finding room to extend the event callback table so it can handle the eight additional events (total size 0xF8); lastly and largely optionally, writing functions to handle any interrupts you intend to catch, assigning them as callbacks for the events.
  The events themselves are caught and cleared, but the table has not be resized or relocated to support handling the interrupts.  For now callbacks for these events cannot be implemented.
  It may be interesting to implement this in a roundabout way by mapping its handler as a cart interrupt event, as that effectively acts as an override.
  The feature is sidelined; it isn't necessary unless, say, the internal debug interface is converted to use USB or something.
* SI DMA handler (Don't Care)
  Normally KeepAlive is called on each SI read.  Without this, the game will return to the iQue menus automatically within a few microseconds, much in the way CIC timeouts functioned.
  The hackit.sys patch removes this requirement, and right now you're...unlikely...to run things without it.
* PI E/DMA (Don't Care)
  EDMA read requests that start on the last halfword or end on the first halfword of a page are special-handled, effectively becoming word-by-word reads.  GoldenEye's EDMA support is...peculiar--it doesn't test or change PI settings--and is only directly used when a disk drive is present.  It also lacks the dedicated extended hardware word read function used for this (though write is supported).  For now, this feature is sidelined.

-Zoinkity
